﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;

namespace LinkedInApi
{
    public partial class ApiClient
    {
        private ILogger logger;

        private List<ApiError> errors;

        public IEnumerable<ApiError> Errors
        {
            get
            {
                return this.errors.AsEnumerable();
            }
        }

        public bool HasErrors
        {
            get
            {
                return this.errors.Any();
            }
        }

        public ApiError GetLastError()
        {
            return this.errors.LastOrDefault();
        }

        public ApiClient(ILogger logger = null)
        {
            this.logger = logger == null ? CreateConsoleLogger() : logger;
            this.errors = new List<ApiError>();
            this.logger.Log("Api", "Initialized");
        }

        public async Task<AccessToken> GetAccessToken(string clientID, string secretKey, string redirectUri, string login, string password, string scope = null, int timeout = 30000)
        {
            logger.Log("Api", "GetAccessToken()");
            this.errors.Clear();
            
            if (timeout <= 0)
                timeout = 30000;

            if (!String.IsNullOrWhiteSpace(scope))
                scope = "&scope=" + scope;
            else
                scope = "";
            
            var authorizationCode = await OAuth2.GetAuthorizationCode(clientID, redirectUri, login, password, scope, logger, timeout);
            
            if (OAuth2.HasError)
                this.errors.Add(OAuth2.LastError);

            if (String.IsNullOrWhiteSpace(authorizationCode))
                return null;

            logger.Log("Api", "Authorization Code received");
            logger.Log("Api", "ApiWebClient initialization");
            using(var w = new ApiWebClient())
            {
                NameValueCollection data = new NameValueCollection();
                data.Add("grant_type", "authorization_code");
                data.Add("code", authorizationCode);
                data.Add("redirect_uri", redirectUri);
                data.Add("client_id", clientID);
                data.Add("client_secret", secretKey);

                logger.Log("ApiWebClient", "Call access token");
                byte[] answer = null; 

                try
                {
                    answer = await w.UploadValuesTaskAsync("https://www.linkedin.com/uas/oauth2/accessToken", data, timeout);
                }
                catch (Exception e)
                {
                    this.errors.Add(new ApiError(e));
                    logger.Log("ApiWebClient", e);
                    return null;
                }

                logger.Log("ApiWebClient", "Answer received");
                dynamic json = ParseAnswer(answer);

                if (answer != null && json != null && json.access_token != null && json.expires_in != null)
                {
                    try
                    {
                        var token = new AccessToken((string)json.access_token, (int)json.expires_in);
                        logger.Log("Api", "Token received, expiries {0}", token.ExpireDate);

                        return token;
                    }
                    catch (Exception e)
                    {
                        this.errors.Add(new ApiError(e));
                        logger.Log("Api", e);
                    }
                }
                else
                {
                    logger.Log("Api", "Invalid answer: {0}", answer != null ? System.Text.Encoding.UTF8.GetString(answer) : "null");
                }
            }
            return null;
        }

        public async Task<List<string[]>> GetBasicProfileFields(AccessToken atoken, string[] fields = null, int timeout = 30000)
        {
            logger.Log("Api", "GetBasicFields()");
            this.errors.Clear();

            if (atoken == null)
            {
                logger.Log("Api", "AccessToken is null");
                this.errors.Add(new ApiError(new ArgumentException("AccessToken is null", "AccessToken")));
                return null;
            }

            if (fields != null)
                fields = fields.Where(f => !String.IsNullOrWhiteSpace(f)).ToArray();

            var url = String.Format("https://api.linkedin.com/v1/people/~{0}?format=json", fields != null && fields.Length > 0 ? ":(" + String.Join(",",fields) + ")" : "");
            
            logger.Log("Api", "ApiWebClient initialization");
            using(var w = new ApiWebClient(atoken))
            {
                logger.Log("ApiWebClient", "GET {0}", url);
                byte[] answer = null; 

                try
                {
                    answer = await w.DownloadDataTaskAsync(url, 30000);
                }
                catch (Exception e)
                {
                    this.errors.Add(new ApiError(e));
                    logger.Log("ApiWebClient", e);
                    return null;
                }

                logger.Log("ApiWebClient", "Answer received");
                dynamic json = ParseAnswer(answer);

                if (answer != null && json != null)
                {
                    logger.Log("Api", "Json parsed, fill data");

                    try
                    {
                        var result = new List<string[]>();
                        result.Add((json as JObject).Properties().Select(p => p.Name).ToArray());
                        result.Add((json as JObject).PropertyValues().Select(v => v.ToString()).ToArray());

                        return result;
                    }
                    catch (Exception e)
                    {
                        this.errors.Add(new ApiError(e));
                        logger.Log("Api", e);
                    }
                }
                else
                {
                    logger.Log("Api", "Invalid answer: {0}", answer != null ? System.Text.Encoding.UTF8.GetString(answer) : "null");
                }
            }
            return null;
        }


        private object ParseAnswer(byte[] json)
        {
            if (json != null)
            {
                try
                {
                    return JObject.Parse(System.Text.Encoding.UTF8.GetString(json));
                }
                catch (Exception e)
                {
                    this.errors.Add(new ApiError(e));
                    logger.Log("Api", "ParseAnswer error: {0}, json: {1}", e.ToString(), System.Text.Encoding.UTF8.GetString(json));
                }
            }
            return null;
        }
    }
}
